package edu.ccut.saturn.component.impl.node;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import edu.ccut.saturn.component.ComponentManager;
import edu.ccut.saturn.component.ILogicContext;
import edu.ccut.saturn.component.IParameterContext;
import edu.ccut.saturn.component.IParameterInfo;
import edu.ccut.saturn.component.SaturnDebugThreadsManager;
import edu.ccut.saturn.component.impl.SaturnParameterInfo;
import edu.ccut.saturn.component.loader.node.NodeInfoLoader;
import edu.ccut.saturn.component.loader.node.StartIterationNodeInfoLoader;
import edu.ccut.saturn.component.utils.ExceptionUtils;
import edu.ccut.saturn.component.xml.ISaturnElement;
import edu.ccut.saturn.component.xml.SaturnElement;

/**
 * 循环开始结点位于logic组件内，被logic组件调用。 循环开始结点表示一个迭代过程的开始，它与循环结束结点一起限定了一段迭代过程的范围。 目前支持对<code>java.util.Collection</code>类型进行循环遍历操作。
 * 循环开始结点要执行对集合的遍历操作，即操纵迭代器对象获得集合中的元素，并保存到上下文环境中以便后继结点的操作；
 * 同时还要判断是否结束循环，并在上下文中设置变量指示对应的迭代结束结点是否返回继续进行循环操作。
 * <p>
 * <code>StartIteratorNodeInfo</code>扩展自<code>AbstractNodeInfo</code>：
 * <li>字段<code>listKey</code>：记录了用户指定的待迭代的集合在逻辑上下文中的存储位置；
 * <li>字段<code>itemKey</code>：记录了用户指定的迭代过程中每个当前访问的元素在逻辑上下文中的存储位置；
 * <li>字段<code>endId</code>：记录了与这个循环开始结点相对应的循环结束结点的id；
 * <li>字段<code>iteratorKey</code>：保存了待迭代集合的迭代器对象；
 * <li>字段<code>iteratorHasNextId</code>：上下文中保存是否进行下次迭代的位置。
 * 
 * @author 谷钰
 * @author 温建
 * 
 * @version 1.0 2008.8.15
 * 
 * @see AbstractNodeInfo
 * @see ILogicContext
 * 
 */
public class StartIteratorNodeInfo extends AbstractNodeInfo {

	private static final String ITERATION_HAS_NEXT = "__ITERATOR_HAS_NEXT__";

	private static final String ITERATION_KEY = "__ITERATOR_KEY__";

	private String listKey;

	private String itemKey;

	private String endId;

	private String iteratorKey;

	private String iteratorHasNextId;

	public StartIteratorNodeInfo(String id, String name, String description,
			String nextId, String listKey, String itemKey, String endId,
			String x, String y) {

		super(id, name, description, nextId, null, x, y, "start-iteration");
		this.listKey = listKey;
		this.itemKey = itemKey;
		this.endId = endId;
		this.iteratorHasNextId = id + ITERATION_HAS_NEXT;
		this.iteratorKey = id + ITERATION_KEY;
	}

	/**
	 * 获得循环开始结点保存的待迭代集合在上下文中的键值
	 * 
	 * @return
	 */
	public String getListKey() {
		return listKey;
	}

	/**
	 * 设置循环开始结点保存的待迭代集合在上下文中的键值
	 * 
	 * @param listKey
	 */
	public void setListKey(String listKey) {
		this.listKey = listKey;
	}

	/**
	 * 获得循环开始结点保存的待迭代集合中当前访问的元素在上下文中的键值
	 * 
	 * @return
	 */
	public String getItemKey() {
		return itemKey;
	}

	/**
	 * 设置循环开始结点保存的待迭代集合中当前访问的元素在上下文中的键值
	 * 
	 * @param listKey
	 */
	public void setItemKey(String itemKey) {
		this.itemKey = itemKey;
	}

	/**
	 * 获得与循环开始结点对应的循环结束结点的id
	 * 
	 * @return
	 */
	public String getEndId() {
		return endId;
	}

	/**
	 * 设置与循环开始结点对应的循环结束结点的id
	 * 
	 * @param listKey
	 */
	public void setEndId(String endId) {
		this.endId = endId;
	}

	/**
	 * 迭代开始结点的执行逻辑。 首先通过判断上下文中的变量iteratorKey是否为空，来判断在当前流程中是不是第一次执行迭代开始结点。
	 * 如果变量iteratorKey的值是空则表示第一次执行迭代开始结点，则进行初始化工作，需要获得待遍历集合，并将
	 * 集合的迭代器对象放到上下文中。如果不是第一次执行则不需要进行初始化工作。
	 * 然后判断上下文中的迭代器对象，如果是空则设置继续循环的标识为”false“，并将下一运行结点设置为对应的循环结束结点。
	 * 如果迭代器对象不是空，则判断集合中是否有下一元素未被遍历，如果有则取出下一元素并放到上下文中，并设置继续循环的标识为”true“；并执行循环体中的结点。
	 * 如果迭代器对象没有下一元素需要遍历，则设置继续循环的标识为”false“，删除上下文中的迭代器对象，并将下一运行结点设置为对应的循环结束结点。
	 * 
	 * @param logicContext
	 *            逻辑上下文对象，记录结点运行时的上下文信息
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void doExecute(ILogicContext logicContext) throws Exception {
		ComponentManager.beforeExecuteComponent(this, this
				.createParameterContext(logicContext));

		try {
			this.initalIterator(logicContext);

			if (logicContext.getValue(iteratorKey) == null) {
				logicContext.setValue(iteratorHasNextId, "false");
				logicContext.setNodeRouter(this.getId(), endId);

			} else if (((Iterator) logicContext.getValue(iteratorKey))
					.hasNext()) {

				logicContext.setValue(this.itemKey, ((Iterator) logicContext
						.getValue(iteratorKey)).next());
				// TODO:当前的做法：判断迭代器是否有next元素来判断是否继续循环，会多执行一次迭代开始和迭代结束结点。
				logicContext.setValue(iteratorHasNextId, "true");

			} else {
				logicContext.setValue(iteratorHasNextId, "false");
				logicContext.removeParameterKey(iteratorKey, 1);
				logicContext.setNodeRouter(this.getId(), endId);
			}
			logger
					.debug("      node : {execute : '{result : '"
							+ ("true".equals((String) (logicContext
									.getValue(iteratorHasNextId))) ? " continue iterator"
									: " over itartor") + "'}', process : '" + logicContext.getId() + "'}");

		} catch (Exception e) {
			logger.error("ERROR : {message : '" + ExceptionUtils.getExMessage(e) + "', process : '" + logicContext.getId() + "', node : '"
					+ super.getNodeType() + " " + super.getId() + " " + super.getName() + "'}" );
			throw e;

		} finally {
			ComponentManager.afterExecuteComponent(this,
					createParameterContext(logicContext));
		}
	}

	@SuppressWarnings("unchecked")
	private void initalIterator(ILogicContext context) {

		if (context.getValue(iteratorKey) == null) {
			Object list = context.getValue(this.listKey);

			if (list == null) {
				return;
			}

			if (list instanceof Collection) {
				Collection collection = (Collection) list;
				context.setValue(iteratorKey, collection.iterator());
			}
		}
	}

	protected IParameterContext createParameterContext(
			ILogicContext logicContext) throws Exception {

		if (SaturnDebugThreadsManager.getInstance().isStart()) {
			initParameterInfos();
			return logicContext.createParameterContext(getParameterInfos());
		}
		return null;
	}

	private void initParameterInfos() {
		List<IParameterInfo> parameters = new ArrayList<IParameterInfo>();
		SaturnParameterInfo listParameter = new SaturnParameterInfo();
		listParameter.setClassType(Collection.class);
		listParameter.setId("1");
		listParameter.setName("list");
		listParameter.setPassingType(IParameterInfo.PASSING_IN);
		listParameter.setScope(IParameterInfo.SCOPE_LOGIC);
		listParameter.setValueKey(this.listKey);
		parameters.add(listParameter);

		SaturnParameterInfo itemParameter = new SaturnParameterInfo();
		itemParameter.setClassType(Object.class);
		itemParameter.setId("2");
		itemParameter.setName("item");
		itemParameter.setPassingType(IParameterInfo.PASSING_IN_OUT);
		itemParameter.setScope(IParameterInfo.SCOPE_LOGIC);
		itemParameter.setValueKey(this.itemKey);
		parameters.add(itemParameter);

		SaturnParameterInfo hasNextParameter = new SaturnParameterInfo();
		hasNextParameter.setClassType(String.class);
		hasNextParameter.setId("3");
		hasNextParameter.setName("has_next");
		hasNextParameter.setPassingType(IParameterInfo.PASSING_IN_OUT);
		hasNextParameter.setScope(IParameterInfo.SCOPE_LOGIC);
		hasNextParameter.setValueKey(this.iteratorHasNextId);
		parameters.add(hasNextParameter);
		super.setParameterInfos(parameters);
	}

	public ISaturnElement toSaturnElement() {
		ISaturnElement startIteratorElement = new SaturnElement(
				StartIterationNodeInfoLoader.KEY_NODETYPE_ITERATION_START);

		startIteratorElement.addAttribute(NodeInfoLoader.KEY_NODE_ID, this
				.getId());

		startIteratorElement.addAttribute(NodeInfoLoader.KEY_NODE_NEXT, this
				.getNextId());

		startIteratorElement.addAttribute(NodeInfoLoader.KEY_NODE_X, this
				.getX());

		startIteratorElement.addAttribute(NodeInfoLoader.KEY_NODE_Y, this
				.getY());

		ISaturnElement nameElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_NAME, this.getName());

		ISaturnElement iteratorListKeyElement = new SaturnElement(
				StartIterationNodeInfoLoader.KEY_ITERATION_LIST_KEY,
				this.getListKey());

		ISaturnElement itemKeyElement = new SaturnElement(
				StartIterationNodeInfoLoader.KEY_ITERATION_ITEM_KEY,
				this.itemKey);

		ISaturnElement endIdElement = new SaturnElement(
				StartIterationNodeInfoLoader.KEY_ITERATION_END_ID, this.endId);

		ISaturnElement descriptionElement = new SaturnElement(
				StartIterationNodeInfoLoader.KEY_NODE_DESCRIPTION, this
						.getDescription());

		startIteratorElement.addSubElement(nameElement);
		startIteratorElement.addSubElement(iteratorListKeyElement);
		startIteratorElement.addSubElement(itemKeyElement);
		startIteratorElement.addSubElement(endIdElement);
		startIteratorElement.addSubElement(descriptionElement);
		return startIteratorElement;
	}
}